﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ConfigCreate.Model;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Data;
using static System.Environment;
using Newtonsoft.Json;
using System.Dynamic;
using System.ComponentModel;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using Brush = System.Windows.Media.Brush;

namespace ConfigCreate.Controls
{
    /// <summary>
    /// ControlContentPanel.xaml 的交互逻辑
    /// </summary>
    public partial class ControlContentPanel : System.Windows.Controls.UserControl
    {
        /// <summary>
        /// 配置保存时参数变化通过事件
        /// </summary>
        public event EventHandler<ParameterChange[]>? SaveParametersChange;
        /// <summary>
        /// 动态绑定通知
        /// </summary>
        public Action<DynamicBindingRoutePath[]>? DynamicBindingNotification { get; set; }

        //大类关联字典
        private ConcurrentDictionary<string, HeaderData> con_list = new ConcurrentDictionary<string, HeaderData>();
        //基础控件对应字典，用于查找基础控件
        private ConcurrentDictionary<GroupHeader, CreateControlBasePanel> panel_list = new ConcurrentDictionary<GroupHeader, CreateControlBasePanel>();
        //控件间隐藏关联列表
        private ConcurrentDictionary<CreateControlBasePanel, VisibilityPanel> visibility_panel_list = new ConcurrentDictionary<CreateControlBasePanel, VisibilityPanel>();
        //影响其它控件可见性的关联列表
        private ConcurrentDictionary<CreateControlBasePanel, HashSet<CreateControlBasePanel>> panel_binding_list = new ConcurrentDictionary<CreateControlBasePanel, HashSet<CreateControlBasePanel>>();
        //组控件隐藏关联列表
        private ConcurrentDictionary<CreateControlGroup, VisibilityPanel> visibility_group_list = new ConcurrentDictionary<CreateControlGroup, VisibilityPanel>();
        //影响其它组控件可见性的关联列表
        private ConcurrentDictionary<CreateControlBasePanel, HashSet<CreateControlGroup>> group_binding_list = new ConcurrentDictionary<CreateControlBasePanel, HashSet<CreateControlGroup>>();
        //安全备份拷贝
        private ConcurrentDictionary<object, object> copy_list = new ConcurrentDictionary<object, object>();
        //安全备份拷贝反转配对,跟上面的字典是反过来的
        private ConcurrentDictionary<object, object> copy_reverse_list = new ConcurrentDictionary<object, object>();
        //对象备份还原位置列表
        private ConcurrentDictionary<object, string> config_list = new ConcurrentDictionary<object, string>();
        private string? dynamic_binding_config_file = null;

        /// <summary>
        /// 
        /// </summary>
        public ControlContentPanel()
        {
            InitializeComponent();

            //检查自定义控件注册绑定
            if (!DataTemplateManager.IsRegistered && DataTemplateManager.RegList.Count > 0)
            {
                foreach (var key in DataTemplateManager.RegList.Keys)
                    this.Resources.Add(key, DataTemplateManager.RegList[key]);
            }
        }

        #region 属性
        /// <summary>
        /// 标题
        /// </summary>
        public static DependencyProperty PanelTypeProperty = DependencyProperty.Register("PanelType", typeof(ContentPanelType), typeof(ControlContentPanel), new FrameworkPropertyMetadata(ContentPanelType.SingleRow));
        /// <summary>
        /// 标题
        /// </summary>
        public ContentPanelType PanelType
        {
            get { return (ContentPanelType)GetValue(PanelTypeProperty); }
            set { SetValue(PanelTypeProperty, value);  }
        }

        #endregion

        /// <summary>
        /// 自检查函数列表
        /// </summary>
        public HashSet<Func<string?>> SelfCheckList { get; } = new HashSet<Func<string?>>();

        /// <summary>
        /// 确定完成前做最后操作函数列表
        /// </summary>
        public HashSet<Action> CompletionList { get; } = new HashSet<Action>();

        /// <summary>
        /// 默认开启安全拷贝机制，只有在保存时才真正更新到对象上
        /// </summary>
        public bool SafeCopyInstance { get; set; } = true;

        /// <summary>
        /// 当前用户角色名
        /// </summary>
        public string? RoleName { get; set; }

        /// <summary>
        /// 动态绑定路径
        /// </summary>
        public DynamicBindingRoutePath[]? DynamicBindings { get; private set; }

        /// <summary>
        /// Tab项
        /// </summary>
        public ObservableCollection<TabItem> TabItems { get; private set; } = new ObservableCollection<TabItem>();

        private GroupHeader CheckGroupHeader(string header_name, string group_name, string property_path)
        {
            //if (string.IsNullOrEmpty(header_name))
            //    throw new ConfigException("主分类名不能为空");
            //if (string.IsNullOrEmpty(group_name))
            //    throw new ConfigException("分组名不能为空");
            return new GroupHeader(header_name, group_name, property_path);
        }

        private CreateControlGroup GetGroupPanel(string header_name, string group_name, int? group_index, int? header_index)
        {
            var header = con_list.GetOrAdd(header_name, (hname) =>
            {
                HeaderData hdata = new HeaderData(hname);
                if (header_index.HasValue)
                    hdata.HeaderIndex = header_index.Value;
                return hdata;
            });
            var group = header.AddOrGetGroup(group_name, group_index);
            return group;
        }

        private CreateControlBasePanel CreatePanel(GroupHeader gh, string title, bool save_config)
        {
            CreateControlBasePanel panel = new CreateControlBasePanel();
            panel.Title = title;
            panel.GroupHeader = gh;
            panel.Notification = Notification;
            panel.SaveConfig = save_config;
            panel_list[gh] = panel;
            return panel;
        }

        private void Notification(CreateControlBasePanel panel, object value)
        {
            //设置Panel可见性
            if (panel_binding_list.TryGetValue(panel, out var list))
            {
                foreach (var p in list)
                {
                    if (visibility_panel_list.TryGetValue(p, out var vp))
                        SetPanelVisibility(p, vp);

                }
            }
            //设置组可见性
            if (group_binding_list.TryGetValue(panel, out var list2))
            {
                foreach (var g in list2)
                {
                    if (visibility_group_list.TryGetValue(g, out var vp))
                        SetGroupVisibility(g, vp);
                }
            }
        }

        private void SetPanelVisibility(CreateControlBasePanel panel, VisibilityPanel vp)
        {
            if (!panel.RolePermission.ContainsKey(RolePermissionType.Invisible))
                panel.Visibility = vp.CheckVisibility() ? Visibility.Visible : Visibility.Collapsed;
        }

        private void SetGroupVisibility(CreateControlGroup group,VisibilityPanel vp)
        {
            if (!group.RolePermission.ContainsKey(RolePermissionType.Invisible))
                group.Visibility = vp.CheckVisibility() ? Visibility.Visible : Visibility.Collapsed;
        }

        /// <summary>
        /// 获取实际绑定对象
        /// </summary>
        /// <param name="binding_object">原始对象</param>
        /// <returns></returns>
        public object GetSetObject(object binding_object)
        {
            if(SafeCopyInstance)
            {
                return copy_list.GetOrAdd(binding_object, obj =>
                {
                    var clone = ObjectCloner.ObjectCloner.DeepClone(obj);
                    copy_reverse_list.TryAdd(clone, binding_object);
                    return clone;
                });
            }
            return binding_object;
        }

        /// <summary>
        /// 获取真实绑定对象
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="binding_object">原始对象</param>
        /// <returns>返回绑定对象</returns>
        public T? GetBindingObject<T>(T binding_object) where T : class
        {
            object value = GetSetObject(binding_object);
            if(value!=null)
                return (T)value;
            return default;
        }

        #region 添加控件

        /// <summary>
        /// 添加文本框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="text_lenght">文本框中字符长度</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <param name="is_readonly">是否为只读</param>
        /// <param name="is_multiline">是否多行文本</param>
        /// <param name="height">文本高度</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddTextBox(string header_name, string group_name, object binding_object, string property_path, string title, string? placeholder = null, int? text_lenght = null, bool show_clear_button = true, bool is_readonly = false, bool is_multiline = false, double height = 60d, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddTextBox(obj, property_path, placeholder, text_lenght, show_clear_button, is_readonly, is_multiline, height);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加数字框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="decimal_places">小数点多少位</param>
        /// <param name="increment">获取或设置单击向上或向下按钮时，数字显示框（也称作 up-down 控件）递增或递减的值</param>
        /// <param name="maximum">获取或设置最大允许值</param>
        /// <param name="minimum">获取或设置最小允许值</param>
        /// <param name="value_format">数字显示格式</param>
        /// <param name="show_clear_button">是否显示清除按钮</param>
        /// <param name="is_readonly">是否为只读</param>
        /// <param name="rule">数据验证</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddNumeric(string header_name, string group_name, object binding_object, string property_path, string title, string? placeholder = null, int? decimal_places = null, double? increment = null, double? maximum = null, double? minimum = null, string? value_format = null, bool show_clear_button = false, bool is_readonly = false, ValidationRule? rule = null, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddNumeric(obj, property_path, placeholder, decimal_places, increment, maximum, minimum, value_format, show_clear_button, is_readonly, rule);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加切换按钮
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="checked_txt">选择时显示的文本</param>
        /// <param name="unchecked_txt">未选择时显示的文本</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddToggleButton(string header_name, string group_name, object binding_object, string property_path, string title, string? checked_txt = null, string? unchecked_txt = null, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddToggleButton(obj, property_path, checked_txt, unchecked_txt);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加密码框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="password_len">密码框字符长度</param>
        /// <param name="show_eye_button">是否显示查看密码按钮</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <param name="password_char">密码字符</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddPasswordBox(string header_name, string group_name, object binding_object, string property_path, string title, string? placeholder = null, int? password_len = null, bool show_eye_button = true, bool show_clear_button = true, char? password_char = null, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddPasswordBox(obj, property_path, placeholder, password_len, show_eye_button, show_clear_button, password_char);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加组合框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="item_type">添加项类型</param>
        /// <param name="tl">数据源</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddComboBox(string header_name, string group_name, object binding_object, string property_path, string title, Type item_type, List<ComboBoxData> tl, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddComboBox(obj, property_path, item_type, tl);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加日期选择框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddDatePicker(string header_name, string group_name, object binding_object, string property_path, string title, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddDatePicker(obj, property_path);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加日期时间选择框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="date_timeformat">日期显示格式</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddDateTimePicker(string header_name, string group_name, object binding_object, string property_path, string title, string? date_timeformat = null, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddDateTimePicker(obj, property_path, date_timeformat);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加多选组合框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="item_type">子项类型</param>
        /// <param name="tl">数据源</param>
        /// <param name="show_select_all_button">是否显示选择所有框</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddMultipleComboBox(string header_name, string group_name, object binding_object, string property_path, string title, Type item_type, List<ComboBoxData> tl, bool show_select_all_button = true, bool show_clear_button = true, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddMultipleComboBox(obj, property_path, item_type, tl, show_select_all_button, show_clear_button);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加IP地址框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="is_readonly">是否只读模式</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddIpAddressTextBox(string header_name, string group_name, object binding_object, string property_path, string title, bool is_readonly = false, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddIpAddressTextBox(obj, property_path, is_readonly);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加打开文本选择框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="dialog_title">打开对话框标题</param>
        /// <param name="dialog_filter">打开对话框过滤项</param>
        /// <param name="is_multiselect">是否为多选</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddOpenFile(string header_name, string group_name, object binding_object, string property_path, string title, string? dialog_title = null, string? dialog_filter = null, bool is_multiselect = false, bool is_readonly = false, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddOpenFile(obj, property_path, dialog_title, dialog_filter, is_multiselect, is_readonly);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加文本保存对话框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="dialog_title">对话框标题</param>
        /// <param name="dialog_filter">对话框过滤项</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        /// <param name="default_ext">默认扩展名</param>
        /// <param name="initial_directory">初始化目录</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddSaveFile(string header_name, string group_name, object binding_object, string property_path, string title, string? dialog_title = null, string? dialog_filter = null, bool is_readonly = false, string? default_ext = null, string? initial_directory = null, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddSaveFile(obj, property_path, dialog_title, dialog_filter, is_readonly, default_ext, initial_directory);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加目录选择框
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="dialog_description">对话框标题</param>
        /// <param name="show_new_folder_button">是否显示新建文件夹按钮</param>
        /// <param name="root_folder">对话框根目录</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddOpenFolder(string header_name, string group_name, object binding_object, string property_path, string title, string? dialog_description = null, bool show_new_folder_button = true, SpecialFolder root_folder = SpecialFolder.Desktop, bool is_readonly = false, int? group_index = null, int? header_index = null, bool necessary = false, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddOpenFolder(obj, property_path, dialog_description, show_new_folder_button, root_folder, is_readonly);
            panel.Necessary = necessary;
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加标签框控件
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="style_key">样式Key</param>
        /// <param name="horizontal_alignment">横向靠齐方式</param>
        /// <param name="font_size">文字大小</param>
        /// <param name="foreground">前渲染</param>
        /// <param name="background">背景渲染</param>
        /// <param name="font_weight">文字样式</param>
        /// <param name="font_family">文字字体</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddTextBlock(string header_name, string group_name, object binding_object, string property_path, string? style_key = null, HorizontalAlignment horizontal_alignment = HorizontalAlignment.Left, double? font_size = null, Brush? foreground = null, Brush? background = null, string? font_weight = null, string? font_family = null, int? group_index = null, int? header_index = null, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, "", save_config);
            panel.AddTextBlock(binding_object, property_path, style_key, horizontal_alignment, font_size, foreground, background, font_weight, font_family);
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        /// <summary>
        /// 添加自定义控件
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="title">标题</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddCustomControl(string header_name, string group_name, object binding_object, string property_path, string? title = null, int? group_index = null, int? header_index = null, string? remark = null, bool save_config = true)
        {
            object obj = GetSetObject(binding_object);
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            CreateControlBasePanel panel = CreatePanel(gh, title, save_config);
            panel.AddCustomControl(binding_object, property_path, string.IsNullOrEmpty(title));
            panel.Remark = remark ?? string.Empty;
            CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
            group.AddControlPanel(panel);
        }

        #endregion

        #region 绑定

        /// <summary>
        /// 设置或添加控件的绑定模式
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="binding_route_path">绑定路由</param>
        /// <param name="binding_data_func">生成绑定数据源的函数</param>
        /// <param name="default_binding">是否默认显示绑定模式</param>
        /// <param name="show_switch_button">是否显示模式切换按钮</param>
        /// <param name="title">标题</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddOrSetBindingPanel(string header_name, string group_name, string property_path, string binding_route_path, Func<object, GroupHeader, List<BindingData>>? binding_data_func, bool default_binding = true, bool show_switch_button = true, string title = "未设置标题", int? group_index = null, int? header_index = null, bool? necessary = null, string? remark = null, bool save_config = true)
        {
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            if (!panel_list.TryGetValue(gh, out var panel)) //没有找到就创建一个
            {
                panel = CreatePanel(gh, title, save_config);
                if (string.IsNullOrEmpty(panel.PropertyPath))
                    panel.PropertyPath = property_path;
                panel.IsFormBinding = true;
                CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
                group.AddControlPanel(panel);
            }
            else
            {
                panel.IsFormBinding = default_binding;
            }
            if (necessary.HasValue)
                panel.Necessary = necessary.Value;
            if (!string.IsNullOrEmpty(remark))
                panel.Remark = remark ?? string.Empty;
            panel.ShowSwitchBut = show_switch_button;
            panel.BindingRoutePath = binding_route_path;
            panel.BindingDataFunc = binding_data_func;
        }

        /// <summary>
        /// 设置或添加控件的绑定模式
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="binding_route_path">绑定路由</param>
        /// <param name="binding_data">绑定列表</param>
        /// <param name="default_binding">是否默认显示绑定模式</param>
        /// <param name="show_switch_button">是否显示模式切换按钮</param>
        /// <param name="title">标题</param>
        /// <param name="group_index">组排序索引</param>
        /// <param name="header_index">大类别排序索引</param>
        /// <param name="necessary">是否为必填项</param>
        /// <param name="remark">备注</param>
        /// <param name="save_config">该属性是否在保存时保存到配置中</param>
        public void AddOrSetBindingPanel(string header_name, string group_name, string property_path, string binding_route_path, List<BindingData>? binding_data, bool default_binding = true, bool show_switch_button = true, string title = "未设置标题", int? group_index = null, int? header_index = null, bool? necessary = null, string? remark = null, bool save_config = true)
        {
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            if (!panel_list.TryGetValue(gh, out var panel)) //没有找到就创建一个
            {
                panel = CreatePanel(gh, title, save_config);
                if (string.IsNullOrEmpty(panel.PropertyPath))
                    panel.PropertyPath = property_path;
                panel.IsFormBinding = true;
                CreateControlGroup group = GetGroupPanel(header_name, group_name, group_index, header_index);
                group.AddControlPanel(panel);
            }
            else
            {
                panel.IsFormBinding = default_binding;
            }
            if (necessary.HasValue)
                panel.Necessary = necessary.Value;
            if (!string.IsNullOrEmpty(remark))
                panel.Remark = remark ?? string.Empty;
            panel.ShowSwitchBut = show_switch_button;
            panel.BindingRoutePath = binding_route_path;
            panel.DataList = binding_data ?? new List<BindingData>();
        }

        #endregion

        #region 权限设置

        /// <summary>
        /// 设置控件权限
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="role_name">角色名</param>
        /// <param name="type">设置类型，不可见或不可用</param>
        /// <returns></returns>
        public bool SetRolePermissionPanel(string header_name, string group_name, string property_path, string role_name, RolePermissionType type = RolePermissionType.Invisible)
        {
            GroupHeader gh = new GroupHeader(header_name, group_name, property_path);
            if (!panel_list.TryGetValue(gh, out var panel)) //不存在，退出
                return false;
            panel.RolePermission.AddOrUpdate(type, new HashSet<string>() { role_name }, (t, tl) =>
            {
                tl.Add(role_name);
                return tl;
            });
            return true;
        }

        /// <summary>
        /// 设置组权限
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="role_name">角色名</param>
        /// <param name="type">设置类型，不可见或不可用</param>
        /// <returns></returns>
        public bool SetRolePermissionGroup(string header_name, string group_name, string role_name, RolePermissionType type = RolePermissionType.Invisible)
        {
            if (!con_list.TryGetValue(header_name, out var header))
                return false;
            if (!header.TryGetGroup(group_name, out var group))
                return false;
            if (group == null)
                return false;
            group.RolePermission.AddOrUpdate(type, new HashSet<string>() { role_name }, (t, tl) =>
            {
                tl.Add(role_name);
                return tl;
            });
            return true;
        }

        /// <summary>
        /// 设置大类别权限
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="role_name">角色名</param>
        /// <param name="type">设置类型，不可见或不可用</param>
        /// <returns></returns>
        public bool SetRolePermissionHeader(string header_name, string role_name, RolePermissionType type = RolePermissionType.Invisible)
        {
            if (!con_list.TryGetValue(header_name, out var header))
                return false;
            header.RolePermission.AddOrUpdate(type, new HashSet<string>() { role_name }, (t, tl) =>
            {
                tl.Add(role_name);
                return tl;
            });
            return true;
        }

        #endregion

        #region 其它功能设置

        /// <summary>
        /// 控制控件的可见性
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="binding_property_path">用于关联控制的同组属性的属性名</param>
        /// <param name="value">关联控制属性的检查值</param>
        /// <param name="is_reversal">是否条件反转</param>
        /// <returns></returns>
        public bool SetVisibilityPanel(string header_name, string group_name, string property_path, string binding_property_path, object value, bool is_reversal = false)
        {
            return SetVisibilityPanel(header_name, group_name, property_path, header_name, group_name, binding_property_path, value, is_reversal);
        }

        /// <summary>
        /// 控制控件的可见性
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="binding_header_name">用于关联控制属性所在大类别名</param>
        /// <param name="binding_group_name">用于关联控制属性所在的组名</param>
        /// <param name="binding_property_path">用于关联控制属性的属性名</param>
        /// <param name="value">关联控制属性的检查值</param>
        /// <param name="is_reversal">是否条件反转</param>
        /// <returns></returns>
        /// <exception cref="ConfigException"></exception>
        public bool SetVisibilityPanel(string header_name, string group_name, string property_path, string binding_header_name, string binding_group_name, string binding_property_path, object value, bool is_reversal = false)
        {
            GroupHeader gh = new GroupHeader(header_name, group_name, property_path);
            if (!panel_list.TryGetValue(gh, out var panel)) //不存在，退出
                return false;
            GroupHeader gh_binding = new GroupHeader(binding_header_name, binding_group_name, binding_property_path);
            if (!panel_list.TryGetValue(gh_binding, out var panel_binding))
                return false;
            if (gh.Equals(gh_binding))
                throw new ConfigException("控件不能控制自身可见！");
            panel_binding.IsContentChangedNotification = true;
            visibility_panel_list.AddOrUpdate(panel, new VisibilityPanel(panel_binding, value, is_reversal), (p, vp) =>
            {
                vp.AddPanelAndValue(panel_binding, value, is_reversal);
                return vp;
            });
            panel_binding_list.AddOrUpdate(panel_binding, new HashSet<CreateControlBasePanel>() { panel }, (p, tl) =>
            {
                tl.Add(panel);
                return tl;
            });
            return true;
        }

        /// <summary>
        /// 控制组的可见性
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="binding_header_name">用于关联控制属性所在大类别名</param>
        /// <param name="binding_group_name">用于关联控制属性所在的组名</param>
        /// <param name="binding_property_path">用于关联控制属性的属性名</param>
        /// <param name="value">关联控制属性的检查值</param>
        /// <param name="is_reversal">是否条件反转</param>
        /// <returns></returns>
        /// <exception cref="ConfigException"></exception>
        public bool SetVisibilityGroup(string header_name, string group_name, string binding_header_name, string binding_group_name, string binding_property_path, object value, bool is_reversal = false)
        {
            if (header_name == binding_header_name && group_name == binding_group_name)
                throw new ConfigException("自身组内的控件不能控制自身可见性");
            GroupHeader gh_binding = new GroupHeader(binding_header_name, binding_group_name, binding_property_path);
            if (!panel_list.TryGetValue(gh_binding, out var binding_panel))
                return false;
            if (!con_list.TryGetValue(header_name, out var header))
                return false;
            if (!header.TryGetGroup(group_name, out var group))
                return false;
            if (group == null)
                return false;
            binding_panel.IsContentChangedNotification = true;
            visibility_group_list.AddOrUpdate(group, new VisibilityPanel(binding_panel, value, is_reversal), (g, vp) =>
            {
                vp.AddPanelAndValue(binding_panel, value, is_reversal);
                return vp;
            });
            group_binding_list.AddOrUpdate(binding_panel, new HashSet<CreateControlGroup>() { group }, (p, tl) =>
            {
                tl.Add(group);
                return tl;
            });
            return true;
        }

        /// <summary>
        /// 设置控件值修改通知
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">绑定对象的属性名</param>
        /// <param name="definition">通知时控件标识</param>
        /// <returns></returns>
        public bool SetEditNotificationPanel(string header_name, string group_name, string property_path, string? definition = null)
        {
            GroupHeader gh = new GroupHeader(header_name, group_name, property_path);
            if (!panel_list.TryGetValue(gh, out var panel)) //不存在，退出
                return false;
            if (string.IsNullOrEmpty(definition))
                definition = panel.Title;
            panel.EditNotificationDefinition = definition;
            return true;
        }

        /// <summary>
        /// 添加保存时各控件时数值的自检查函数
        /// </summary>
        /// <param name="func">检查函数</param>
        public void AddSelfCheckFunc(Func<string?> func)
        {
            SelfCheckList.Add(func);
        }

        /// <summary>
        /// 添加确定完成前做最后操作函数
        /// </summary>
        /// <param name="action"></param>
        public void AddPreCompletion(Action action)
        {
            CompletionList.Add(action);
        }

        /// <summary>
        /// 设置组排序和宽高
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="dock">设置组的依靠</param>
        /// <param name="group_width">组宽度</param>
        /// <param name="group_height">组高度</param>
        /// <param name="parent_key">父容器Key</param>
        /// <returns></returns>
        public bool SetDockPanelGroup(string header_name, string group_name, Dock dock, double group_width = double.NaN, double group_height = double.NaN, string? parent_key = null)
        {
            if (!con_list.TryGetValue(header_name, out var header))
                return false;
            if (!header.TryGetGroup(group_name, out var group))
                return false;
            if (group != null)
            {
                this.PanelType = ContentPanelType.DockPanel;
                DockPanel.SetDock(group, dock);
                if (group_width != double.NaN)
                    group.Width = group_width;
                if (group_height != double.NaN)
                    group.Height = group_height;
                if (!string.IsNullOrEmpty(parent_key))
                    group.ParentKey = parent_key;
            }
            return true;
        }

        /// <summary>
        /// 添加DockPanel面板
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="dockpane_key">DockPanel面板Key</param>
        /// <param name="dock">依靠方式</param>
        /// <param name="width">面板宽度</param>
        /// <param name="height">面板高度</param>
        /// <param name="parent_key">面板的父容器Key</param>
        /// <returns></returns>
        public bool AddDockPanel(string header_name, string dockpane_key, Dock dock, double width = double.NaN, double height = double.NaN, string? parent_key = null)
        {
            if (!con_list.TryGetValue(header_name, out var header))
                return false;

            header.AddOrGetDockPanelData(dockpane_key, dock, parent_key, width, height);
            return true;
        }

        #endregion

        /// <summary>
        /// 更新控件状态
        /// </summary>
        protected void UpdateVisibilityPanels()
        {
            foreach (var p in visibility_panel_list.Keys)
                SetPanelVisibility(p, visibility_panel_list[p]);
        }

        /// <summary>
        /// 更新组状态
        /// </summary>
        protected void UpdateVisibilityGroups()
        {
            foreach (var g in visibility_group_list.Keys)
                SetGroupVisibility(g, visibility_group_list[g]);

        }

        /// <summary>
        /// 更新权限下各模式的状态
        /// </summary>
        protected void UpdateRolePermission()
        {
            //设置组权限
            foreach (var header in con_list.Values)
            {
                foreach(var group in header.GetGroups())
                {
                    if (!group.CheckRolePermission(RolePermissionType.Invisible, RoleName))
                        group.Visibility = Visibility.Collapsed;
                    if (!group.CheckRolePermission(RolePermissionType.UnEnabled, RoleName))
                        group.IsEnabled = false;
                }
            }
            //设置Panel权限
            foreach(var panel in panel_list.Values)
            {
                if (!panel.CheckRolePermission(RolePermissionType.Invisible, RoleName))
                    panel.Visibility = Visibility.Collapsed;
                if (!panel.CheckRolePermission(RolePermissionType.UnEnabled, RoleName))
                    panel.IsEnabled = false;
            }
        }

        /// <summary>
        /// 扫描类实例来添加控件并设置控件
        /// </summary>
        /// <typeparam name="T">类</typeparam>
        /// <param name="class_obj">类对象</param>
        /// <param name="errmsg">输出错误信息</param>
        /// <param name="scan_children">是否扫描子属性</param>
        /// <param name="null_for_create">如果子属性值为空是否自动创建</param>
        /// <param name="config_file">配置存储位置</param>
        /// <param name="action">添加控件后执行的代码</param>
        /// <exception cref="ConfigException"></exception>
        public bool AddControlSource<T>(T class_obj, out string errmsg, bool scan_children = true, bool null_for_create = true, string? config_file = null, Action? action = null) where T : class
        {
            if (!string.IsNullOrEmpty(config_file))
            {
                if (!System.IO.Path.IsPathRooted(config_file))
                    config_file = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", config_file);
            }
            else if(class_obj.GetType().GetCustomAttributes(typeof(CreateConfigPathAttribute),true).FirstOrDefault() is CreateConfigPathAttribute config_path)
            {
                config_file = config_path.ConfigPath;
                if (!System.IO.Path.IsPathRooted(config_file))
                    config_file = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", config_file);
            }
            if (config_file != null)
            {
                if (!config_list.TryAdd(class_obj, config_file))
                {
                    errmsg = "对象实例已扫描添加，不能重复添加";
                    return false;
                }
            }

            return this.ConverterObject<T>(class_obj, out errmsg, scan_children, null_for_create, config_file, action);
        }

        /// <summary>
        /// 通过配置文件生成控件
        /// </summary>
        /// <param name="dynamic_binding_config_file">保存和还原动态绑定的文件</param>
        /// <param name="safe_copy_instance">安全拷贝，按确认才生效</param>
        /// <param name="role_name">角色名</param>
        public void ShowControlsWithConfigFile(string dynamic_binding_config_file, bool safe_copy_instance = true, string? role_name = null)
        {
            if (string.IsNullOrEmpty(dynamic_binding_config_file))
                throw new ConfigException("储存绑定状态的配置文件名不能为空");
            DynamicBindingRoutePath[]? binding_source = null;
            if (!System.IO.Path.IsPathRooted(dynamic_binding_config_file))
                dynamic_binding_config_file = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config", dynamic_binding_config_file);
            if (System.IO.File.Exists(dynamic_binding_config_file))
            {
                string json = System.IO.File.ReadAllText(dynamic_binding_config_file);
                binding_source = JsonConvert.DeserializeObject<DynamicBindingRoutePath[]>(json);
            }

            this.dynamic_binding_config_file = dynamic_binding_config_file;
            ShowControls(safe_copy_instance, role_name, binding_source);
        }

        /// <summary>
        /// 显示控件
        /// </summary>
        /// <param name="safe_copy_instance">安全模式，只有在点击确认后才更新到对象中</param>
        /// <param name="role_name">角色名</param>
        /// <param name="binding_source">绑定模式下的数据源还原</param>
        /// <exception cref="ConfigException"></exception>
        public void ShowControls(bool safe_copy_instance = true, string? role_name = null, DynamicBindingRoutePath[]? binding_source = null!)
        {
            SafeCopyInstance = safe_copy_instance;
            if (con_list.Count == 0)
                throw new ConfigException("请先添加控件");
            RoleName = role_name;
            TabItems.Clear();

            //还原状态
            if (binding_source != null)
            {
                foreach(var panel in panel_list.Values)
                {
                    if (binding_source.Any(it => it.CheckPanel(panel)))
                    {
                        DynamicBindingRoutePath dbrp = binding_source.FirstOrDefault(it => it.CheckPanel(panel));
                        panel.IsFormBinding = true;
                        panel.BindingRoutePath = dbrp.BindingRoutePath;
                    }
                    else
                    {
                        panel.IsFormBinding = false;
                    }
                }
            }

            TabItem? item = null;
            

            foreach (HeaderData header in con_list.Values.OrderBy(it => it.HeaderIndex).ThenBy(it => it.HeaderId))
            {
                if (header.CheckRolePermission(RolePermissionType.Invisible, role_name))
                {
                    item = new TabItem();
                    item.Header = header.HeaderName;
                    ScrollViewer sv = new ScrollViewer();
                    System.Windows.Controls.Panel? sp = null;
                    switch (PanelType)
                    {
                        case ContentPanelType.SingleRow:
                            sp = new StackPanel();
                            break;
                        case ContentPanelType.DockPanel:
                            sp = new DockPanel() { LastChildFill = true };
                            
                            break;
                        default:
                            sp = new StackPanel();
                            break;
                    }
                    foreach (CreateControlGroup group in header.GetGroups())
                    {
                        if (PanelType == ContentPanelType.DockPanel)
                        {
                            if (string.IsNullOrEmpty(group.ParentKey))
                            {
                                sp.Children.Add(group);
                            }
                            else if(group.ParentKey != null)
                            {
                                var pp = header.AddDockPanelDatas(group.ParentKey, (DockPanel)sp);
                                pp.DockPanel.Children.Add(group);
                            }
                        }
                        else
                        {
                            sp.Children.Add(group);
                        }
                    }
                    if (PanelType == ContentPanelType.SingleRow)
                    {
                        Grid grid_bottom = new Grid() { Height = 10 };
                        sp.Children.Add(grid_bottom);
                    }
                    sv.Content = sp;

                    item.Content = sv;
                    TabItems.Add(item);
                    if (!header.CheckRolePermission(RolePermissionType.UnEnabled, role_name))
                        sp.IsEnabled = false;
                }
            }

            if (TabItems.Count > 0)
            {
                TabItems[0].IsSelected = true;

                if (TabItems.Count == 1 && item != null)
                    item.Visibility = Visibility.Collapsed;

                UpdateVisibilityPanels();
                UpdateVisibilityGroups();
                UpdateRolePermission();
            }
        }

        /// <summary>
        /// 保存更新
        /// </summary>
        public bool SaveUpdate(out string errmsg)
        {
            if (!CheckNecessary(out errmsg))
                return false;
            if (!CheckSelfCheckFunc(out errmsg))
                return false;
            if (!GetDynamicBindings(true, out errmsg))
                return false;

            //检查完成，做最后的操作
            foreach(Action action in CompletionList)
                action();

            if (SafeCopyInstance)
            {
                
                List<ParameterChange>? tl = new List<ParameterChange>();
                foreach (var panel in panel_list.Values)
                {
                    RestoreProperty(panel, tl);
                }
                if (tl.Count > 0)
                {
                    SaveParametersChange?.Invoke(this, tl.ToArray());
                    tl.Clear();
                }
                tl = null;
            }

            SaveConfigFile();

            return true;
        }

        /// <summary>
        /// 设置Panel控件标题宽度
        /// </summary>
        /// <param name="width"></param>
        public void SetPanelTitleWidht(double width)
        {
           foreach(var panel in panel_list.Values)
            {
                panel.TitleWidth = width;
            }
        }

        private void SaveConfigFile()
        {
            List<Exception> exceptions= new List<Exception>();
            Dictionary<object, Dictionary<GroupHeader, object?>> savelist = new Dictionary<object, Dictionary<GroupHeader, object?>>();
            foreach(var gh in panel_list.Keys)
            {
                CreateControlBasePanel panel = panel_list[gh];
                if (panel.SaveConfig)
                {
                    object? binding_obj = panel.BindingObject;
                    if (SafeCopyInstance && binding_obj != null)
                        copy_reverse_list.TryGetValue(binding_obj, out binding_obj);
                    if (binding_obj != null && config_list.TryGetValue(binding_obj, out string? config_file) && !string.IsNullOrEmpty(config_file))
                    {
                        if (!savelist.ContainsKey(binding_obj))
                            savelist[binding_obj] = new Dictionary<GroupHeader, object?>();
                        savelist[binding_obj][gh] = panel.PanelValue;
                    }
                }
            }
            //
            string json = string.Empty;
            foreach (var obj in savelist.Keys)
            {
                try
                {
                    if (config_list.TryGetValue(obj, out string? config_file))
                    {
                        System.IO.FileInfo finfo = new System.IO.FileInfo(config_file);
                        if (System.IO.Directory.Exists(finfo.DirectoryName) == false)
                            System.IO.Directory.CreateDirectory(finfo.DirectoryName ?? string.Empty);
                        List<GroupHeaderObject>? tl = savelist[obj].Select(it => new GroupHeaderObject() { GroupHeader = it.Key, Value = it.Value }).ToList();
                        json = JsonConvert.SerializeObject(tl);
                        tl.Clear();
                        tl = null;
                        System.IO.File.WriteAllText(config_file, json);
                    }
                }
                catch(Exception ex)
                {
                    exceptions.Add(ex);
                }
            }
            savelist.Clear();
            config_list.Clear();
            if (exceptions.Count > 0)
                throw new AggregateException(exceptions);
        }

        /// <summary>
        /// 检查自检函数
        /// </summary>
        /// <param name="errmsg"></param>
        /// <returns></returns>
        public bool CheckSelfCheckFunc(out string errmsg)
        {
            errmsg = string.Empty;
            foreach(var func in SelfCheckList)
            {
                errmsg = func();
                if(!string.IsNullOrEmpty(errmsg)) 
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 批量执行所有自检函数并返回结果
        /// </summary>
        /// <returns></returns>
        public bool CheckNecessary(out string errmsg)
        {
            errmsg = string.Empty;
            foreach(var panel in panel_list.Values)
            {
                if (!panel.CheckNecessary())
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append("【");
                    if (!string.IsNullOrEmpty(panel.GroupHeader.HeaderName))
                        sb.Append($"{panel.GroupHeader.HeaderName} - ");
                    if (!string.IsNullOrEmpty(panel.GroupHeader.GroupName))
                        sb.Append($"{panel.GroupHeader.GroupName} - ");
                    sb.Append($"{panel.Title}】为必填项！");
                    errmsg = sb.ToString();
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 获取动态绑定列表
        /// </summary>
        /// <returns></returns>
        public bool GetDynamicBindings(bool notification, out string errmsg)
        {
            errmsg = string.Empty;
            List<DynamicBindingRoutePath>? tl = new List<DynamicBindingRoutePath>();
            foreach (var panel in panel_list.Values)
            {
                if (panel.IsFormBinding)
                {
                    if (panel.Necessary && string.IsNullOrEmpty(panel.BindingRoutePath))
                    {
                        errmsg = $"[{panel.GroupHeader.HeaderName} - {panel.GroupHeader.GroupName} - {panel.Title}]的绑定项未设置";
                        return false;
                    }
                    
                    tl.Add(new DynamicBindingRoutePath(panel.BindingObject, panel.PropertyPath, panel.BindingRoutePath, panel.GroupHeader.HeaderName, panel.GroupHeader.GroupName));
                }
            }
            DynamicBindings = tl.ToArray();
            if (notification)
                DynamicBindingNotification?.Invoke(DynamicBindings);
            if (!string.IsNullOrEmpty(dynamic_binding_config_file))
            {
                System.IO.FileInfo finfo = new System.IO.FileInfo(dynamic_binding_config_file);
                if (!System.IO.Directory.Exists(finfo.DirectoryName))
                    System.IO.Directory.CreateDirectory(finfo.DirectoryName ?? string.Empty);
                string json = JsonConvert.SerializeObject(DynamicBindings);
                System.IO.File.WriteAllText(dynamic_binding_config_file, json);
            }
            tl.Clear();
            tl = null;
            return true;
        }

        /// <summary>
        /// 通过字典把绑定内容
        /// </summary>
        /// <param name="datas"></param>
        /// <param name="errmsg"></param>
        /// <returns></returns>
        public bool UpdateDynamicBindingData(IDictionary<string, object> datas, out string errmsg)
        {
            errmsg = string.Empty;
            if (!GetDynamicBindings(false, out errmsg))
                return false;
            if (DynamicBindings != null)
            {
                foreach (var dbinding in DynamicBindings)
                {
                    if (datas.ContainsKey(dbinding.BindingRoutePath) && dbinding.BindingObject != null)
                    {
                        object value = datas[dbinding.BindingRoutePath];
                        if (SafeCopyInstance)
                        {
                            if (copy_reverse_list.TryGetValue(dbinding.BindingObject, out object? obj))
                                dbinding.SetValue(obj, value);
                        }
                        else
                        {
                            dbinding.SetValue(dbinding.BindingObject, value);
                        }
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 获取基础面板控件
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <returns></returns>
        public CreateControlBasePanel? GetPanel(string header_name, string group_name, string property_path)
        {
            GroupHeader gh = CheckGroupHeader(header_name, group_name, property_path);
            if (panel_list.TryGetValue(gh, out var panel))
                return panel;
            return null;
        }

        /// <summary>
        /// 获取对应生成的控件
        /// </summary>
        /// <param name="header_name">大类别名</param>
        /// <param name="group_name">组名</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <returns></returns>
        public T? GetPanelContent<T>(string header_name, string group_name, string property_path) where T : FrameworkElement
        {
            CreateControlBasePanel? panel = GetPanel(header_name, group_name, property_path);
            if (panel != null)
                return panel.GetVisualContent<T>();
            return null;
        }

        private void RestoreProperty(CreateControlBasePanel panel, List<ParameterChange> tl)
        {
            object? origin = panel.BindingObject;
            if (origin == null)
                return;
            string property_path = panel.PropertyPath;
            GroupHeader gh = panel.GroupHeader;
            if (copy_reverse_list.TryGetValue(origin, out object? target))
            {
                object? value_origin = null;
                object? value_target = null;

                if (target is ExpandoObject)
                {
                    IDictionary<string, object>? dic_origin = origin as IDictionary<string, object>;
                    IDictionary<string, object>? dic_target = target as IDictionary<string, object>;
                    if (dic_origin != null && dic_target != null)
                    {
                        if (dic_origin.TryGetValue(property_path, out value_origin) && dic_target.TryGetValue(property_path, out value_target))
                        {
                            Type obj_type = value_origin.GetType();
                            if (!string.IsNullOrEmpty(panel.EditNotificationDefinition))
                            {
                                if (!CheckObjectEquals(value_origin, value_target, obj_type))
                                {
                                    ParameterChange pc = new ParameterChange()
                                    {
                                        Definition = panel.EditNotificationDefinition,
                                        Origin = ObjectCloner.ObjectCloner.DeepClone(value_target),
                                        Target = ObjectCloner.ObjectCloner.DeepClone(value_origin),
                                        ParameterType = obj_type
                                    };
                                    tl.Add(pc);
                                }
                            }

                            if (value_origin is List<string> tl_origin)
                            {
                                List<string>? tl_target = value_target as List<string>;
                                if (tl_target == null)
                                {
                                    tl_target = new List<string>();
                                    dic_target[property_path] = tl_target;
                                }
                                tl_target.Clear();
                                tl_target.AddRange(tl_origin);
                            }
                            else
                            {
                                dic_target[property_path] = value_origin;
                            }
                        }
                    }
                }
                else
                {
                    var pi = origin.GetType().GetProperty(property_path);
                    if (pi == null | pi?.CanWrite == false || pi?.CanRead == false)
                        return;
                    value_origin = pi?.GetValue(origin);
                    value_target = pi?.GetValue(target);

                    if (!string.IsNullOrEmpty(panel.EditNotificationDefinition))
                    {
                        if (!CheckObjectEquals(value_origin, value_target, pi?.PropertyType))
                        {
                            ParameterChange pc = new ParameterChange()
                            {
                                Definition = panel.EditNotificationDefinition,
                                Origin = ObjectCloner.ObjectCloner.DeepClone(value_target),
                                Target = ObjectCloner.ObjectCloner.DeepClone(value_origin),
                                ParameterType = pi?.PropertyType
                            };
                            tl.Add(pc);
                        }
                    }

                    if (value_origin is List<string> tl_origin)
                    {
                        List<string>? tl_target = value_target as List<string>;
                        if (tl_target == null)
                        {
                            tl_target = new List<string>();
                            pi?.SetValue(target, tl_target);
                        }
                        tl_target.Clear();
                        tl_target.AddRange(tl_origin);
                    }
                    else
                    {
                        pi?.SetValue(target, value_origin);
                    }
                }
            }
        }

        private bool CheckObjectEquals(object? origin, object? target, Type? type)
        {
            if (origin != null && target == null)
                return false;
            if (target != null && origin == null)
                return false;
            if (origin != null && target != null)
            {
                if(type?.Equals(typeof(List<string>)) == true)
                {
                    List<string> tl1 = (List<string>)origin;
                    List<string> tl2 = (List<string>)target;
                    if (tl1.Count != tl2.Count)
                        return false;
                    foreach (string tl in tl1)
                    {
                        if (!tl2.Contains(tl))
                            return false;
                    }
                    return true;
                }
                return origin.Equals( target);
            }
            return true;
        }
    }
}
